/**
 * Copyright(c) 2015 Jade Techonologies Co., Ltd.
 *
 * History:
 *   14-11-15 上午10:47 Created by lyyang
 */
package com.jade.framework.base.log4j;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.RollingFileAppender;
import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;

/**
 * 支持以日期作为目录的appender
 *
 * @author <a href="mailto:lyyang@jade.com">lyyang</a>
 * @version 1.0 14-11-15 上午10:47
 */
public class DailyRollingFileAppender
        extends RollingFileAppender
{
    private long nextRollover = 0;
    private static Map<String, BeginFileData> fileMaps = new HashMap<String, BeginFileData>();
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");

    // synchronization not necessary since doAppend is alreasy synched
    public void rollOver()
    {

        File target;
        File file;
        int maxBackupIndexLeng = String.valueOf(maxBackupIndex).length();
        if (qw != null) {
            long size = ((CountingQuietWriter) qw).getCount();
            LogLog.debug("rolling over count=" + size);
            //   if operation fails, do not roll again until
            //      maxFileSize more bytes are written
            nextRollover = size + maxFileSize;
        }
        LogLog.debug("maxBackupIndex=" + maxBackupIndex);

        String nowDateString = sdf.format(new Date());
        String newFileName = (fileName.indexOf(".") != -1 ? fileName.substring(0, fileName.lastIndexOf(".")) :
                fileName);

        boolean renameSucceeded = true;
        // If maxBackups <= 0, then there is no file renaming to be done.
        if (maxBackupIndex > 0) {

            // Delete the oldest file, to keep Windows happy.
            file = new File(newFileName + '.' + nowDateString + '.' + getIndex(maxBackupIndex, maxBackupIndexLeng));

            if (file.exists()) {
                renameSucceeded = file.delete();
            }
            // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
            for (int i = maxBackupIndex - 1; (i >= 1 && renameSucceeded); i--) {
                file = new File(newFileName + '.' + nowDateString + '.' + getIndex(i, maxBackupIndexLeng));
                if (file.exists()) {
                    target = new File(newFileName + '.' + nowDateString + '.' + getIndex(i + 1, maxBackupIndexLeng));
                    LogLog.debug("Renaming file " + file + " to " + target);
                    renameSucceeded = file.renameTo(target);
                }
            }

            if (renameSucceeded) {
                BeginFileData beginFileData = fileMaps.get(fileName);
                //在每天一个日志目录的方式下，检测日期是否变更了，如果变更了就要把变更后的日志文件拷贝到变更后的日期目录下。
                if (newFileName.indexOf(nowDateString) == -1 &&
                        beginFileData.getFileName().indexOf("yyyyMMdd") != -1) {
                    newFileName = beginFileData.getFileName().replace("yyyyMMdd", nowDateString);
                    newFileName = (newFileName.indexOf(".") != -1 ?
                            newFileName.substring(0, newFileName.lastIndexOf(".")) : newFileName);
                }
                target = new File(newFileName + '.' + nowDateString + '.' + getIndex(1, maxBackupIndexLeng));
                this.closeFile();
                file = new File(fileName);
                LogLog.debug("Renaming file " + file + " to " + target);

                renameSucceeded = file.renameTo(target);
                //
                //   if file rename failed, reopen file with append = true
                //
                if (!renameSucceeded) {
                    try {
                        this.setFile(fileName, true, bufferedIO, bufferSize);
                    }
                    catch (IOException e) {
                        LogLog.error("setFile(" + fileName + ", true) call failed.", e);
                    }
                }
            }
        }
        //
        //   if all renames were successful, then
        //
        if (renameSucceeded) {

            try {
                // This will also close the file. This is OK since multiple
                // close operations are safe.

                this.setFile(fileName, false, bufferedIO, bufferSize);
                nextRollover = 0;
            }
            catch (IOException e) {
                LogLog.error("setFile(" + fileName + ", false) call failed.", e);
            }
        }
    }


    /**
     * 文件个数的长度补零，如果文件个数为10那么文件的个数长度就是2位，第一个文件就是01，02，03....
     *
     * @param i
     * @param maxBackupIndexLeng
     * @return
     */
    private String getIndex(int i, int maxBackupIndexLeng)
    {
        String index = String.valueOf(i);
        int len = index.length();
        for (int j = len; j < maxBackupIndexLeng; j++) {
            index = "0" + index;
        }
        return index + ".log";
    }


    /**
     * This method differentiates RollingFileAppender from its super
     * class.
     *
     * @since 0.9.0
     */
    protected void subAppend(LoggingEvent event)
    {
        super.subAppend(event);
        if (fileName != null && qw != null) {

            String nowDate = sdf.format(new Date());
            //检测日期是否已经变更了，如果变更了就要重创建日期目录
            if (!fileMaps.get(fileName).getDate().equals(nowDate)) {
                rollOver();
                return;
            }

            long size = ((CountingQuietWriter) qw).getCount();
            if (size >= maxFileSize && size >= nextRollover) {
                rollOver();
            }
        }
    }


    @Override
    public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
            throws IOException
    {

        String nowDate = sdf.format(new Date());
        //如果文件路径包含了“yyyyMMdd”就是每天一个日志目录的方式记录日志(第一次的时候)
        if (fileName.indexOf("yyyyMMdd") != -1) {
            String beginFileName = fileName;
            fileName = fileName.replace("yyyyMMdd", nowDate);
            fileMaps.put(fileName, new BeginFileData(beginFileName, nowDate));
        }
        BeginFileData beginFileData = fileMaps.get(fileName);
        //检测日期是否已经变更了，如果变更了就要把原始的字符串给fileName变量，把变更后的日期做为开始日期
        if (!beginFileData.getDate().equals(nowDate)) {
            //获取出第一次的文件名
            beginFileData.setDate(nowDate);
            fileName = beginFileData.getFileName().replace("yyyyMMdd", nowDate);
            fileMaps.put(fileName, beginFileData);
        }

        //D:/data/test/yyyyMMdd/test.log 替换yyyyMMdd为当前日期。

        super.setFile(fileName, append, this.bufferedIO, this.bufferSize);
    }


    class BeginFileData
    {

        public BeginFileData(String fileName, String date)
        {
            super();
            this.fileName = fileName;
            this.date = date;
        }

        private String fileName;
        private String date;

        public String getFileName()
        {
            return fileName;
        }

        public void setFileName(String fileName)
        {
            this.fileName = fileName;
        }

        public String getDate()
        {
            return date;
        }

        public void setDate(String date)
        {
            this.date = date;
        }
    }


}
